package eztools

import (
	"sort"
)

type pairsCommon struct {
	next int
}

// Rewind used after Add() to reset and before GetNMove() to the first item
func (ps *pairsCommon) Rewind() {
	ps.next = 0
}

// PairsSS is a collection of string-string
type PairsSS struct {
	pair []struct{ id, val string }
	pairsCommon
}

// PairsIS is a collection of int-string
type PairsIS struct {
	pair []struct {
		id  int
		val string
	}
	pairsCommon
}

// PairsII is a collection of int-int
type PairsII struct {
	pair []struct{ id, val int }
	pairsCommon
}

// PairsII is a collection of int-int
type PairsFF struct {
	pair []struct{ id, val float32 }
	pairsCommon
}

// Add appends values to the collection,
//   moving cursor to the next
func (ps *PairsSS) Add(id, val string) {
	ps.pair = append(ps.pair, struct{ id, val string }{id, val})
	ps.next++
}

// Next gets the current item in the collection and moves to the next
//   call Rewind() after Add() to reset to and get the first item
// error=ErrOutOfBound when the end met
func (ps *PairsSS) GetNMove() (string, string, error) {
	if ps.next >= len(ps.pair) || len(ps.pair) < 1 {
		return "", "", ErrOutOfBound
	}
	defer func() {
		ps.next++
	}()
	return ps.pair[ps.next].id, ps.pair[ps.next].val, nil
}

// Len gets size of the collection
func (ps PairsSS) Len() int {
	return len(ps.pair)
}

// Get get values of an ellement in the collection
func (ps PairsSS) Get(index int) (string, string, error) {
	if index >= len(ps.pair) || index < 0 {
		return "", "", ErrOutOfBound
	}
	return ps.pair[index].id, ps.pair[index].val, nil
}

// Set sets values of an ellement in the collection
func (ps *PairsSS) Set(index int, id, val string) error {
	if index >= len(ps.pair) || index < 0 {
		return ErrOutOfBound
	}
	ps.pair[index].id = id
	ps.pair[index].val = val
	return nil
}

// SetPrev sets values of previous ellement in the collection,
//   after calling GetNMove()
func (ps *PairsSS) SetPrev(id, val string) error {
	if ps.next < 1 {
		return ErrOutOfBound
	}
	ps.pair[ps.next-1].id = id
	ps.pair[ps.next-1].val = val
	return nil
}

// FindId find the first value whose **id** matches input
func (ps PairsSS) FindId(s string) (string, error) {
	for _, v := range ps.pair {
		if v.id == s {
			return v.val, nil
		}
	}
	return "", ErrNoValidResults
}

// Add appends values to the collection,
//   moving cursor to the next
func (ps *PairsIS) Add(id int, val string) {
	ps.pair = append(ps.pair, struct {
		id  int
		val string
	}{id, val})
	ps.next++
}

// Next gets the current item in the collection and moves to the next
//   call Rewind() after Add() to reset to and get the first item
// error=ErrOutOfBound when the end met
func (ps *PairsIS) GetNMove() (int, string, error) {
	if ps.next >= len(ps.pair) || len(ps.pair) < 1 {
		return InvalidID, "", ErrOutOfBound
	}
	defer func() {
		ps.next++
	}()
	return ps.pair[ps.next].id, ps.pair[ps.next].val, nil
}

// Len gets size of the collection
func (ps PairsIS) Len() int {
	return len(ps.pair)
}

// Get get values of an ellement in the collection
func (ps PairsIS) Get(index int) (int, string, error) {
	if index >= len(ps.pair) || index < 0 {
		return InvalidID, "", ErrOutOfBound
	}
	return ps.pair[index].id, ps.pair[index].val, nil
}

// Set get values of an ellement in the collection
func (ps *PairsIS) Set(index, id int, val string) error {
	if index >= len(ps.pair) || index < 0 {
		return ErrOutOfBound
	}
	ps.pair[index].id = id
	ps.pair[index].val = val
	return nil
}

// FindStr find the first ID whose **value** matches input
// return value: ErrNoValidResults when none found
func (ps PairsIS) FindStr(s string) (int, error) {
	for _, v := range ps.pair {
		if v.val == s {
			return v.id, nil
		}
	}
	return InvalidID, ErrNoValidResults
}

// Sort sorts the collection by id
func (ps *PairsIS) Sort() {
	sort.Slice(ps.pair, func(i, j int) bool {
		return ps.pair[i].id < ps.pair[j].id
	})
}

// Add appends values to the collection,
//   moving cursor to the next
func (ps *PairsII) Add(id, val int) {
	ps.pair = append(ps.pair, struct{ id, val int }{id, val})
	ps.next++
}

// Next moves to the next item in PairsInt and return the values
//   call Rewind() after Add() to reset to and get the first item
// error=ErrOutOfBound when the end met
func (ps *PairsII) GetNMove() (int, int, error) {
	if ps.next >= len(ps.pair) || len(ps.pair) < 1 {
		return InvalidID, InvalidID, ErrOutOfBound
	}
	defer func() {
		ps.next++
	}()
	return ps.pair[ps.next].id, ps.pair[ps.next].val, nil
}

// Len gets size of the collection
func (ps PairsII) Len() int {
	return len(ps.pair)
}

// Get get values of an ellement in the collection
func (ps PairsII) Get(index int) (int, int, error) {
	if index >= len(ps.pair) || index < 0 {
		return InvalidID, InvalidID, ErrOutOfBound
	}
	return ps.pair[index].id, ps.pair[index].val, nil
}

// Set get values of an ellement in the collection
func (ps *PairsII) Set(index, id, val int) error {
	if index >= len(ps.pair) || index < 0 {
		return ErrOutOfBound
	}
	ps.pair[index].id = id
	ps.pair[index].val = val
	return nil
}

// Add appends values to the collection,
//   moving cursor to the next
func (ps *PairsFF) Add(id, val float32) {
	ps.pair = append(ps.pair, struct{ id, val float32 }{id, val})
	ps.next++
}

// Next moves to the next item in PairsInt and return the values
//   call Rewind() after Add() to reset to and get the first item
// error=ErrOutOfBound when the end met
func (ps *PairsFF) GetNMove() (float32, float32, error) {
	if ps.next >= len(ps.pair) || len(ps.pair) < 1 {
		return InvalidID, InvalidID, ErrOutOfBound
	}
	defer func() {
		ps.next++
	}()
	return ps.pair[ps.next].id, ps.pair[ps.next].val, nil
}

// Len gets size of the collection
func (ps PairsFF) Len() int {
	return len(ps.pair)
}

// Get get values of an ellement in the collection
func (ps PairsFF) Get(index int) (float32, float32, error) {
	if index >= len(ps.pair) || index < 0 {
		return InvalidID, InvalidID, ErrOutOfBound
	}
	return ps.pair[index].id, ps.pair[index].val, nil
}

// Set get values of an ellement in the collection
func (ps *PairsFF) Set(index int, id, val float32) error {
	if index >= len(ps.pair) || index < 0 {
		return ErrOutOfBound
	}
	ps.pair[index].id = id
	ps.pair[index].val = val
	return nil
}
